import {PortalHost} from '@gorhom/portal';
import React, {useEffect, useMemo, useRef, useState} from 'react';
import type {FlatList} from 'react-native';
import {View} from 'react-native';
import type {OnyxCollection} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import DragAndDropProvider from '@components/DragAndDrop/Provider';
import MoneyRequestReportView from '@components/MoneyRequestReportView/MoneyRequestReportView';
import ScreenWrapper from '@components/ScreenWrapper';
import {useSearchContext} from '@components/Search/SearchContext';
import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import useReportIsArchived from '@hooks/useReportIsArchived';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SearchFullscreenNavigatorParamList} from '@libs/Navigation/types';
import {getFilteredReportActionsForReportView, getIOUActionForTransactionID, getOneTransactionThreadReportID, getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {isValidReportIDFromPath} from '@libs/ReportUtils';
import Navigation from '@navigation/Navigation';
import ReactionListWrapper from '@pages/home/ReactionListWrapper';
import {createTransactionThreadReport, openReport} from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ActionListContextType, ScrollPosition} from '@src/pages/home/ReportScreenContext';
import {ActionListContext} from '@src/pages/home/ReportScreenContext';
import type SCREENS from '@src/SCREENS';
import type {Policy, Transaction, TransactionViolations} from '@src/types/onyx';
import {getEmptyObject} from '@src/types/utils/EmptyObject';

type SearchMoneyRequestPageProps = PlatformStackScreenProps<SearchFullscreenNavigatorParamList, typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT>;

const defaultReportMetadata = {
    isLoadingInitialReportActions: true,
    isLoadingOlderReportActions: false,
    hasLoadingOlderReportActionsError: false,
    isLoadingNewerReportActions: false,
    hasLoadingNewerReportActionsError: false,
    isOptimisticReport: false,
};

function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) {
    const {shouldUseNarrowLayout} = useResponsiveLayout();
    const styles = useThemeStyles();
    const {isOffline} = useNetwork();

    const reportIDFromRoute = getNonEmptyStringOnyxID(route.params?.reportID);
    const {currentSearchHash} = useSearchContext();
    const [snapshot] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchHash}`, {canBeMissing: true});

    const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});

    const snapshotReport = useMemo(() => {
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`] ?? {}) as typeof report;
    }, [snapshot, reportIDFromRoute]);

    // Use snapshot report currency if main collection doesn't have it (for offline mode)
    const reportToUse = useMemo(() => {
        if (!report) {
            return report;
        }
        return {...report, currency: report.currency ?? snapshotReport?.currency};
    }, [report, snapshotReport?.currency]);

    const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {canBeMissing: true, allowStaleData: true});
    const [policies = getEmptyObject<NonNullable<OnyxCollection<Policy>>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, canBeMissing: false});
    const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];
    const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true});
    const isReportArchived = useReportIsArchived(report?.reportID);

    const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute, isReportArchived);

    const [scrollPosition, setScrollPosition] = useState<ScrollPosition>({});
    const flatListRef = useRef<FlatList>(null);
    const actionListValue = useMemo((): ActionListContextType => ({flatListRef, scrollPosition, setScrollPosition}), [flatListRef, scrollPosition, setScrollPosition]);

    const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`, {canBeMissing: true});
    const {reportActions: unfilteredReportActions} = usePaginatedReportActions(reportIDFromRoute);
    const {transactions: allReportTransactions, violations: allReportViolations} = useTransactionsAndViolationsForReport(reportIDFromRoute);
    const reportActions = useMemo(() => getFilteredReportActionsForReportView(unfilteredReportActions), [unfilteredReportActions]);
    const reportTransactions = useMemo(() => getAllNonDeletedTransactions(allReportTransactions, reportActions), [allReportTransactions, reportActions]);
    const visibleTransactions = useMemo(
        () => reportTransactions?.filter((transaction) => isOffline || transaction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE),
        [reportTransactions, isOffline],
    );
    const reportTransactionIDs = useMemo(() => visibleTransactions?.map((transaction) => transaction.transactionID), [visibleTransactions]);
    const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActions ?? [], isOffline, reportTransactionIDs);
    const oneTransactionID = reportTransactions.at(0)?.transactionID;

    const reportID = report?.reportID;

    // Prevents creating duplicate transaction threads for legacy transactions
    const hasCreatedLegacyThreadRef = useRef(false);

    // Get transaction from search snapshot if not available in main collections
    const {snapshotTransaction, snapshotViolations} = useMemo(() => {
        if (!snapshot?.data || Object.keys(allReportTransactions).length > 0) {
            return {snapshotTransaction: undefined, snapshotViolations: undefined};
        }

        const transactionKey = Object.keys(snapshot.data).find((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION));
        if (!transactionKey) {
            return {snapshotTransaction: undefined, snapshotViolations: undefined};
        }

        const snapshotData = snapshot.data as Record<string, unknown>;
        const transaction = snapshotData[transactionKey] as Transaction;
        const violationKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transaction.transactionID}`;
        const violations = snapshotData[violationKey] as TransactionViolations | undefined;

        return {snapshotTransaction: transaction, snapshotViolations: violations};
    }, [snapshot?.data, allReportTransactions]);

    useEffect(() => {
        if (transactionThreadReportID === CONST.FAKE_REPORT_ID && oneTransactionID) {
            const iouAction = getIOUActionForTransactionID(reportActions, oneTransactionID);
            createTransactionThreadReport(report, iouAction);
            return;
        }

        openReport(reportIDFromRoute, '', [], undefined, undefined, false, [], undefined);
        // We don't want this hook to re-run on the every report change
        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
    }, [reportIDFromRoute, transactionThreadReportID]);

    useEffect(() => {
        hasCreatedLegacyThreadRef.current = false;
    }, [reportIDFromRoute]);

    // Create transaction thread for legacy transactions that don't have one yet.
    // Wait for all data to load to avoid duplicates or stale data when navigating between reports.
    useEffect(() => {
        if (hasCreatedLegacyThreadRef.current || transactionThreadReportID || (Object.keys(allReportTransactions).length !== 1 && !snapshotTransaction)) {
            return;
        }

        // Use main collection transaction or fallback to snapshot
        const transaction = Object.values(allReportTransactions).at(0) ?? snapshotTransaction;
        if (!transaction || (transaction && transaction.reportID !== reportIDFromRoute)) {
            return;
        }

        // Check that reportActions belong to the current report to avoid using stale data from the previous report
        const hasMatchingReportActions = reportActions.some((action) => {
            const iouReportID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUReportID : undefined;
            return iouReportID?.toString() === reportIDFromRoute;
        });

        if (!hasMatchingReportActions && reportActions.length > 1) {
            return;
        }

        const iouAction = getIOUActionForTransactionID(reportActions, transaction.transactionID);
        if (iouAction) {
            return;
        }

        hasCreatedLegacyThreadRef.current = true;

        const violations = allReportViolations[transaction.transactionID] ?? snapshotViolations;
        createTransactionThreadReport(report, undefined, transaction, violations);
    }, [
        allReportTransactions,
        allReportViolations,
        report,
        reportActions,
        reportIDFromRoute,
        reportMetadata?.isLoadingInitialReportActions,
        snapshot,
        snapshotTransaction,
        snapshotViolations,
        transactionThreadReportID,
        visibleTransactions,
    ]);

    // eslint-disable-next-line rulesdir/no-negated-variables
    const shouldShowNotFoundPage = useMemo(
        (): boolean => {
            if (isLoadingApp !== false) {
                return false;
            }

            // eslint-disable-next-line react-compiler/react-compiler
            if (!reportID && !reportMetadata?.isLoadingInitialReportActions) {
                // eslint-disable-next-line react-compiler/react-compiler
                return true;
            }

            return !!reportID && !isValidReportIDFromPath(reportID);
        },
        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
        [reportID, reportMetadata?.isLoadingInitialReportActions],
    );

    if (shouldUseNarrowLayout) {
        return (
            <ActionListContext.Provider value={actionListValue}>
                <ReactionListWrapper>
                    <ScreenWrapper
                        testID={SearchMoneyRequestReportPage.displayName}
                        shouldEnableMaxHeight
                        offlineIndicatorStyle={styles.mtAuto}
                        headerGapStyles={styles.searchHeaderGap}
                    >
                        <FullPageNotFoundView
                            shouldShow={shouldShowNotFoundPage}
                            subtitleKey="notFound.noAccess"
                            subtitleStyle={[styles.textSupporting]}
                            shouldDisplaySearchRouter
                            shouldShowBackButton={shouldUseNarrowLayout}
                            onBackButtonPress={Navigation.goBack}
                        >
                            <DragAndDropProvider isDisabled={isEditingDisabled}>
                                <MoneyRequestReportView
                                    report={reportToUse}
                                    reportMetadata={reportMetadata}
                                    policy={policy}
                                    shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx}
                                    key={report?.reportID}
                                    backToRoute={route.params.backTo}
                                />
                            </DragAndDropProvider>
                        </FullPageNotFoundView>
                    </ScreenWrapper>
                </ReactionListWrapper>
            </ActionListContext.Provider>
        );
    }

    return (
        <ActionListContext.Provider value={actionListValue}>
            <ReactionListWrapper>
                <ScreenWrapper
                    testID={SearchMoneyRequestReportPage.displayName}
                    shouldEnableMaxHeight
                    offlineIndicatorStyle={styles.mtAuto}
                    headerGapStyles={[styles.searchHeaderGap, styles.h0]}
                >
                    <View style={[styles.searchSplitContainer, styles.flexColumn, styles.flex1]}>
                        <FullPageNotFoundView
                            shouldShow={shouldShowNotFoundPage}
                            subtitleKey="notFound.noAccess"
                            subtitleStyle={[styles.textSupporting]}
                            shouldDisplaySearchRouter
                            shouldShowBackButton={shouldUseNarrowLayout}
                            onBackButtonPress={Navigation.goBack}
                        >
                            <DragAndDropProvider isDisabled={isEditingDisabled}>
                                <View style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]}>
                                    <MoneyRequestReportView
                                        report={reportToUse}
                                        reportMetadata={reportMetadata}
                                        policy={policy}
                                        shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx}
                                        key={report?.reportID}
                                        backToRoute={route.params.backTo}
                                    />
                                </View>
                                <PortalHost name="suggestions" />
                            </DragAndDropProvider>
                        </FullPageNotFoundView>
                    </View>
                </ScreenWrapper>
            </ReactionListWrapper>
        </ActionListContext.Provider>
    );
}

SearchMoneyRequestReportPage.displayName = 'SearchMoneyRequestReportPage';

export default SearchMoneyRequestReportPage;
